import numpy as np
from .adapters import InMemoryAdapter


class AnomalyDetection:
    def __init__(self, max_data, min_data, outlier, adapter=None):
        self.max_data = max_data
        self.min_data = min_data
        self.outlier = outlier
        adapter = adapter or InMemoryAdapter()
        self.adapter = adapter
        self._init()

    def _init(self):
        self.adapter.create_table('user_min_max')
        self.adapter.create_table('test_user')
        self.user_min_max = self.adapter.select_table('user_min_max')
        self.test_user = self.adapter.select_table('test_user')

    # 训练
    def train(self, user_data):
        for key, value in user_data.items():
            cnt_list = np.array([int(i) for i in list(value) if int(i) <= self.outlier])
            length = len(cnt_list)
            if length >= 1:
                avg = cnt_list.sum() / length
                s = ((cnt_list * cnt_list).sum() / length - avg ** 2) ** 0.5
                if s == 0:
                    max_data = max(self.max_data, avg + 3 * 5)
                    min_data = max(self.min_data, avg - 3 * 5)
                    self.user_min_max[key] = [min_data, max_data]
                elif s <= 20:
                    max_data = max(self.max_data, avg + 3 * s)
                    min_data = max(self.min_data, avg - 3 * s)
                    self.user_min_max[key] = [min_data, max_data]
                else:
                    max_data = max(self.max_data, avg + 3 * 20)
                    min_data = max(self.min_data, avg - 3 * 20)
                    self.user_min_max[key] = [min_data, max_data]
            else:
                self.user_min_max[key] = [min_data, max_data]

    # 预测
    def detection(self, username):
        user_count = self.test_user.get(username) or 0
        user_count += 1
        self.test_user[username] = user_count

        if username in self.user_min_max:
            user_max = self.user_min_max[username][-1]
        else:
            self.user_min_max[username] = [self.min_data, self.max_data]
            user_max = self.max_data

        return self.score(user_count, user_max)

    @classmethod
    def score(cls, count, max_value):
        """计算分数"""
        if count > max_value:
            return 1. / (1 + np.exp((max_value - count) / max_value))
        return 0

    def anomaly_list(self):
        """返回当前异常用户列表"""
        return [
            username for username, count in self.test_user.items()
            if count > self.user_min_max[username][-1]
        ]

    def clear(self):
        """清空所有数据"""
        self.adapter.drop_database()
        self._init()


# 用于进行用户评分时使用的训练类
class SafetyScoreDetection(AnomalyDetection):

    def _init(self):
        self.adapter.create_table('user_score_min_max')
        self.adapter.create_table('test_score_user')
        self.user_min_max = self.adapter.select_table('user_score_min_max')
        self.test_user = self.adapter.select_table('test_score_user')

